package com.sec.android.app.myfiles.operation;

import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnKeyListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.Toast;

import com.sec.android.app.myfiles.R;
import com.sec.android.app.myfiles.facade.MyFilesFacade;
import com.sec.android.app.myfiles.facade.cmd.DecompressRecordsCmd;
import com.sec.android.app.myfiles.facade.cmd.DeleteRecordCmd;
import com.sec.android.app.myfiles.feature.PrivateModeMgr;
import com.sec.android.app.myfiles.fragment.AbsMyFilesFragment;
import com.sec.android.app.myfiles.info.AppConstants;
import com.sec.android.app.myfiles.module.abstraction.FileRecord;
import com.sec.android.app.myfiles.operation.compress.CompressOptions;
import com.sec.android.app.myfiles.operation.progressboard.DefaultProgressBoard;
import com.sec.android.app.myfiles.operation.progressboard.ProgressBoard;
import com.sec.android.app.myfiles.util.FileUtils;

import java.util.Locale;


public class FileOperationFragment extends DialogFragment {
    private static final String DIALOG_TAG = "file_operation_dialog";

    private Context mContext;

    private AbsMyFilesFragment mFragment;
    private FragmentManager mFragmentManager;
    private FileOperator mFileOperator;

    private FileRecord mCurDirRecord;
    private boolean mIsDownload = false;

    private ProgressBoard mProgressBoard;
    private PostOperation mFileOpResult;

    private BroadcastReceiver mPrivateReceiver = null;
    FileOperationMediaDbThread mMediaDb;

    public void show() {
        if (mFragmentManager != null) {
            showAllowingStateLoss(mFragmentManager, DIALOG_TAG);
        }
    }

    private void ensureProgressBoard() {
        if (mProgressBoard == null) {
            mProgressBoard = new DefaultProgressBoard(R.layout.file_operation_dialog);
        }
    }

    // If java.lang.IllegalStateException happens when showing a FileOperationFragment,
    // please use this function to avoid IllegalStateException.
    // java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
    public void showAllowingStateLoss(final FragmentManager fm, final String tag) {
        if (fm != null) {
            final FragmentTransaction t = fm.beginTransaction();
            t.add(this, tag);
            t.commitAllowingStateLoss();
        }
    }

    public static FileOperationFragment createInstance(Context context, AbsMyFilesFragment fragment, FragmentManager manager,
                                                       FileOperator.Operation opType, FileOperationArgs args, PostOperation resultCallback) {
        FileOperationFragment ret = new FileOperationFragment();
        ret.initValue(context, fragment, manager, opType, args, resultCallback);
        ret.setCancelable(false);
        return ret;
    }

    public FileOperationFragment() {
    }

    private void initValue(Context context, AbsMyFilesFragment fragment, FragmentManager manager, FileOperator.Operation opType,
                           FileOperationArgs args, PostOperation resultCallback) {
        mContext = context;
        mFragment = fragment;
        mFragmentManager = manager;
        mFileOpResult = resultCallback;

        if (opType == FileOperator.Operation.DELETE &&
                mFragment.getStorageType() == FileRecord.StorageType.Category) {
            mMediaDb = new FileOperationMediaDbThread(context);
            mMediaDb.start();
        }

        mFileOperator = FileOperator.getInstance(fragment.getProcessId(), context, mFragmentManager, mMediaDb);
        if (PrivateModeMgr.getInstance(context).isPrivateMode() && isPrivateModeFileOperation(args)) {
            setBroadcastReceiver();
        }

        switch (opType) {
            case DELETE:
                if (fragment.getNavigationInfo() != null) {
                    if (args.mDeleteType == DeleteRecordCmd.OperationProgress.SHOW_FOLDER_TREE_CONFIRM_POPUP) {
                        mCurDirRecord = FileRecord.createFileRecord(FileRecord.StorageType.Local, args.mDst.getPath());
                    } else {
                        mCurDirRecord = fragment.getNavigationInfo().getCurRecord();
                    }
                }
                break;
            case UNLOCK:
                if (fragment.getNavigationInfo() != null) {
                    mCurDirRecord = fragment.getNavigationInfo().getCurRecord();
                }
                break;
            case COPY:
            case MOVE:
                if (args != null) {
                    mCurDirRecord = args.mDst;
                }
                break;
            case COMPRESS:
                // TODO need to refactoring.
                // find the way to receive compress options from caller.
                if (args != null) {
                    args.mCompressOptions = new CompressOptions();
                    args.mProcessId = fragment.getProcessId();
                    args.mCompressOptions.mCompressMode = CompressOptions.CompressMode.COMPRESS;
                    args.mCompressOptions.mCompressType = CompressOptions.CompressType.ZIP;
                    if (args.mSrcList != null && !args.mSrcList.isEmpty() && args.mSrcList.get(0) != null) {
                        args.mCompressOptions.mSrcRoot = args.mSrcList.get(0).getParent();
                    }
                    mCurDirRecord = FileRecord.createFileRecord(args.mDst.getStorageType(), args.mDst.getPath());
                }
                break;
            case DECOMPRESS:
                // TODO need to refactoring.
                // find the way to receive decompress options from caller.
                if (args.mDecompressType != DecompressRecordsCmd.DecompressType.DECOMPRESS_FROM_PREVIEW) {
                    mCurDirRecord = fragment.getNavigationInfo().getCurRecord();
                }
                args.mCompressOptions = new CompressOptions();
                args.mProcessId = fragment.getProcessId();
                args.mCompressOptions.mCompressMode = CompressOptions.CompressMode.DECOMPRESS;
                args.mCompressOptions.mDecompressToCurrentFolder = args.mDecompressType == DecompressRecordsCmd.DecompressType.DECOMPRESS_CURRENT_FOLDER;
                break;
            default:
                break;
        }
        mFileOperator.setOperationArgs(opType, args);
        mFileOperator.registerProgressListener(mTotalCountProgressListener, mCurFileProgressListener);
    }

    private int getTitleResId() {
        int nRet = 0;
        switch (mFileOperator.getOperation()) {
            case DELETE:
                nRet = R.string.menu_delete;
                break;
            case COPY:
                if (mFileOperator.getArgs().mViewCloudFile) {
                    nRet = R.string.cloudFileOpen;
                } else {
                    nRet = R.string.menu_copy;
                }
                break;
            case MOVE:
                nRet = R.string.menu_move;
                break;
            case COMPRESS:
                nRet = R.string.menu_zip;
                break;
            case DECOMPRESS:
                nRet = R.string.menu_unzip;
                break;
            case PREVIEW_COMPRESS:
                nRet = R.string.processing;
                break;
            case UNLOCK:
                nRet = R.string.unlock;
                break;
            case BACKUP:
                nRet = R.string.menu_backup;
            default:
                break;
        }

        return nRet;
    }

    public void setProgreessBoard(ProgressBoard progressBoard) {
        mProgressBoard = progressBoard;
    }

    private void setVisibleProcessing() {
        switch (mFileOperator.getOperation()) {
            case COPY:
            case MOVE:
            case COMPRESS:
            case DECOMPRESS:
            case DELETE:
                mProgressBoard.showProcessingText(false);
                break;
            default:
                mProgressBoard.showProcessingText(true);
                break;
        }
    }

    // Because of its in the dialog layout, so the parent view its empty
    @SuppressLint("InflateParams")
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        ensureProgressBoard();
        final View root = mProgressBoard.ensureView(getActivity());
        root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                mFileOperator.onGlobalLayoutFinish(root.getHeight());
            }
        });
        setVisibleProcessing();
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        int titleResId = getTitleResId();
        if (titleResId != 0) {
            builder.setTitle(titleResId);
        }
        builder.setView(root);
        builder.setNegativeButton(R.string.cancel, new OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                onCancel(dialog);
            }
        });

        builder.setOnKeyListener(new OnKeyListener() {
            public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
                if (keyCode == KeyEvent.KEYCODE_BACK) {
                    onCancel(dialog);
                    return true;
                }
                return false;
            }
        });
        mFragment.stopObserver();
        mFileOperator.execute();
        return builder.create();
    }

    @Override
    public void onCancel(DialogInterface dialog) {
        mFileOperator.cancel();
        super.onCancel(dialog);
    }

    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        mFragment.startObserver();
        if (mFileOperator.isWorking() && !mFileOperator.isOperationCancelled()) {
            mFileOperator.cancel();
        }

        if (mMediaDb != null) {
            mMediaDb.clear();
        }

        mProgressBoard.clear();
    }

    /**
     * ProgressListener for all file operation
     */
    ProgressListener mTotalCountProgressListener = new ProgressListener() {
        @Override
        public void onProgressChanged(String fileName, int progress, int total) {
            mProgressBoard.setFileNameText(fileName);
            mProgressBoard.setCountText(progress, total);
            mProgressBoard.setHandledSize(mFileOperator.getHandledSize());
        }

        @Override
        public void onFinished(boolean bRet, String msg, Bundle extras) {
            boolean showToast = extras.getBoolean(FileOperator.SHOW_TOAST_ERROR_BUNDLE, true);

            if (!TextUtils.isEmpty(msg)) {
                if (showToast) {
                    Toast.makeText(mContext, msg, bRet ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG).show();
                } else {
                    extras.putString(FileOperator.POPUP_MSG_BUNDLE, msg);
                }
            }

            if (mPrivateReceiver != null) {
                mContext.unregisterReceiver(mPrivateReceiver);
            }

            if (mFragment != null && mCurDirRecord != null) {
                MyFilesFacade._reload(mFragment.getProcessId(), mContext, mCurDirRecord, mFragment, mIsDownload);
            }
            if (mFileOpResult != null) {
                mFileOpResult.onResult(mFileOperator.getOperation(), bRet, extras);
            }
            if (getFragmentManager() != null) {
                dismissAllowingStateLoss();
            }

            if (mProgressBoard.isIndeterminateProgress()) {
                mProgressBoard.setIndeterminateProgress(false);
            }
        }
    };

    /**
     * ProgressListener for A progressing file.
     */
    ProgressListener mCurFileProgressListener = new ProgressListener() {
        @Override
        public void onProgressChanged(String fileName, int progress, int total) {
            String percentString = "";

            switch (progress) {
                case AppConstants.INDETERMINATE_PROGRESS_START:
                    mProgressBoard.setIndeterminateProgress(true);
                    break;
                case AppConstants.INDETERMINATE_PROGRESS_END:
                    mProgressBoard.setIndeterminateProgress(false);
                    progress = 0;
                    break;
            }

            if (!mProgressBoard.isIndeterminateProgress()) {
                mProgressBoard.setProgress(progress);
                percentString = String.format(Locale.getDefault(), "%d", progress) + "%";
            }
            mProgressBoard.setPercentText(percentString);
        }

        @Override
        public void onFinished(boolean bRet, String msg, Bundle extras) {
            // DO NOT NEED TO IMPLEMENT
        }
    };

    public interface PostOperation {
        public void onResult(FileOperator.Operation opType, boolean bRet, Bundle extras);
    }

    private void setBroadcastReceiver() {

        mPrivateReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent == null) return;
            }
        };

    }

    private boolean isPrivateModeFileOperation(FileOperationArgs args) {
        boolean ret = false;

        if (args.mDst != null && FileUtils.isPrivateFolder(args.mDst.getFullPath())) {
            ret = true;
        } else {
            if (args.mSrcList != null) {
                for (FileRecord file : args.mSrcList) {
                    if (file != null) {
                        if (FileUtils.isPrivateFolder(file.getPath())) {
                            ret = true;
                            break;
                        }
                    }
                }
            }
        }

        return ret;
    }
}
